#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include "../include/narytree.h"
#include "../include/vfs.h"
#include "../include/error_code.h"
#include "../include/vfs_errorcodes.h"
#include "../include/global.h"

global* globaldata;

t_node* nary_root_init() {
	t_node* root = (t_node*) malloc(sizeof(t_node));
	root->data = (file_d*) malloc(sizeof(file_d));
	strcpy(root->data->file_path, "/");
	strcpy(root->data->file_name, "/");
	strcpy(root->data->file_type, "DIR");
	root->child = NULL;
	root->siblings = NULL;
	return root;
}

t_node* allocate_node(file_d *fd) {
	t_node* temp = (t_node*) malloc(sizeof(t_node));
	temp->data = (file_d*) malloc(sizeof(file_d));
	temp->data->file_size = fd->file_size;
	strcpy(temp->data->file_name, fd->file_name);
	if(fd->file_path[0]!='\0')
		strcpy(temp->data->file_path, fd->file_path);
	else
		strcpy(temp->data->file_path, "/");
	sprintf(temp->data->file_type, "%s", fd->file_type);
	temp->data->file_block_no = fd->file_block_no;
	temp->data->start_addr = fd->start_addr;
	temp->child = NULL;
	temp->siblings = NULL;
	return temp;
}

int node_searchInSibling(t_node *root, char* data, t_node** resultnode,
		int operation) {
	if (root->siblings == NULL) {
		return UNSUCCESSFUL;
	}
	t_node* node = root;
	while (node->siblings != NULL) {
		if (strcmp(node->siblings->data->file_name, data) == 0) { //&& strcmp(node->siblings->data->file_type, "DIR") == 0
			//printf("\ndata data %s", data);
			if (operation == 1)
				*resultnode = node->siblings;
			else if (operation == 0)
				*resultnode = node;
			return FOUND;
		}
		node = node->siblings;
	}
	return NOTFOUND;
}

int checkDirExist(t_node* root, file_d* data) {
	t_node* node = root;
	while (node != NULL) {
		if (strcmp(node->data->file_name, data->file_name) == 0) {
			return FOUND;
		}
		node = node->siblings;
	}
	return NOTFOUND;
}

file_d* preFileDes(char *path, char *filename, char *filetype) {
	file_d* dir = NULL;
	dir = (file_d*) malloc(sizeof(file_d));
	strcpy(dir->file_name, filename);
	strcpy(dir->file_path, path);
	strcpy(dir->file_type, filetype);
	dir->file_size = 0;
	dir->file_block_no = -1;
	return dir;
}

int insertDirRecursively(int index, int tokens, char path[][CHARSIZE],
		t_node *prevnode, int is_child, file_d *data) {
	t_node *temp_node;
	char *tempPath;
	static int no_fd = 0;
	tempPath = (char*) malloc(sizeof(char) * 1024);

	int i = 0;

	while (i != index) {
		strcat(tempPath, "/");
		strcat(tempPath, path[i]);
		i++;
	}

	file_d* fd = preFileDes(tempPath, path[index], "DIR");
	temp_node = allocate_node(fd);
	temp_node->child = NULL;
	temp_node->siblings = NULL;
	no_fd++;
	if (!is_child) {
		if (prevnode != NULL) {
			prevnode->child = temp_node;
			prevnode = temp_node;
		}
	} else {
		prevnode = prevnode->child;
		while (prevnode != NULL && prevnode->siblings != NULL)
			prevnode = prevnode->siblings;
		prevnode->siblings = temp_node;
		prevnode = temp_node;
	}

	index++;
	if (index != tokens)
		return insertDirRecursively(index, tokens, path, prevnode, 0, data);
	else {
		t_node *new_node = allocate_node(data);
		new_node->child = NULL;
		new_node->siblings = NULL;
		prevnode->child = new_node;
		return SUCCESSFUL;
	}
}

int nary_insert_final(t_node** root, file_d* data, int index) {
	static char path[PATHSIZE][CHARSIZE];
	static int tokens = 0, res = -3;
	t_node* resultnode;
	static t_node *prevnode;
	if (index == -1) {
		char *token;
		tokens = 0;
		char temp[PATHSIZE];
		strcpy(temp, data->file_path);
		token = strtok(temp, "/");
		while (token != NULL) {
			strcpy(path[tokens], token);
			token = strtok(NULL, "/");
			tokens++;
		}
		prevnode = *root;
		res = nary_insert_final(&(*root)->child, data, ++index);

		if (res == FOUND)
			return SUCCESSFUL;
		else
			return res;

	}

	if (index == tokens) {
		if (*root == NULL) {
			*root = allocate_node(data);
			(*root)->child = NULL;
			(*root)->siblings = NULL;
			return SUCCESSFUL;
		} else {
			if (checkDirExist(*root, data) == NOTFOUND) {
				t_node* node = allocate_node(data);
				node->siblings = (*root)->siblings;
				(*root)->siblings = node;
				return SUCCESSFUL;
			} else
				return DUPLICATE;

		}
	} else if ((*root) != NULL
			&& strcmp((*root)->data->file_name, path[index]) == 0
			&& strcmp((*root)->data->file_type, "DIR") == 0) {
		prevnode = *root;
		res = nary_insert_final(&(*root)->child, data, ++index);
		return res;
	} else if ((*root) == NULL && index != tokens) {
		return insertDirRecursively(index, tokens, path, prevnode, 0, data);
	} else {
		resultnode = (t_node*) malloc(sizeof(t_node));
		res = node_searchInSibling(*root, path[index], &resultnode, 1);
		if (res == FOUND) {
			prevnode = resultnode;
			res = nary_insert_final(&(resultnode->child), data, ++index);
			return res;
		} else {
			res = insertDirRecursively(index, tokens, path, prevnode, 1, data);
		}

		if (res == FOUND)
			return SUCCESSFUL;
		return res;

	}

}

int nary_insert_node(t_node** root, file_d* data) {

	int res;
	res = nary_insert_final(root, data, -1);
	return res;
}

void narry_traversal(t_node* root, FILE *ft) {
	if (root != NULL) {
		if (strcmp(root->data->file_path, "/") != 0)
			fprintf(ft, "%s/%s\n", root->data->file_path,
					root->data->file_name);
		else
			fprintf(ft, "%s%s\n", root->data->file_path, root->data->file_name);
		narry_traversal(root->siblings, ft);
		narry_traversal(root->child, ft);

	}
}

int getToken(char filepath[], char path[][CHARSIZE]) {
	int tokens = 0;
	char *token;
	char temp[PATHSIZE];
	strcpy(temp, filepath);
	token = strtok(temp, "/");
	while (token != NULL ) {
		if(token[0]!='\0')
		strcpy(path[tokens], token);
		token = strtok(NULL, "/");
		tokens++;
	}
	return tokens;
}

int nary_list_dir(t_node* root, int flag, char filepath[], char *filenname) {
	static char path[PATHSIZE][CHARSIZE];
	static int tokens = 0;
	static int index = -1;
	static int result = UNSUCCESSFUL;
	static FILE *ft;
	if (index == -1) {
		tokens = getToken(filepath, path);
		index++;
		ft = fopen(filenname, "w+");
		if (ft == NULL) {
			return LISTFILE_CANNOT_CREATE_OUTPUTFILE;
		}
		result = nary_list_dir(root->child, flag, filepath, filenname);
		if (ft != NULL) {
			index = -1;
			tokens = 0;
			fclose(ft);
			ft = NULL;
		}
		return result;
	}

	if (index == tokens) {
		if (root != NULL) {
			if (strcmp(root->data->file_path, "/") != 0)
				fprintf(ft, "%s/%s\n", root->data->file_path,
						root->data->file_name);
			else
				fprintf(ft, "%s%s\n", root->data->file_path,
						root->data->file_name);
			if (strcmp(root->data->file_type, "DIR") == 0 && flag == 1) {
				narry_traversal(root->child, ft);
			}
			root = root->siblings;
			while (root != NULL) {
				if (strcmp(root->data->file_path, "/") != 0)
					fprintf(ft, "%s/%s\n", root->data->file_path,
							root->data->file_name);
				else
					fprintf(ft, "%s%s\n", root->data->file_path,
							root->data->file_name);
				if (strcmp(root->data->file_type, "DIR") == 0 && flag == 1) {
					narry_traversal(root->child, ft);
				}
				root = root->siblings;
			}
			return SUCCESS;
		} else
			return SUCCESS;
	}

	else {
		if (strcmp(root->data->file_name, path[index]) == 0
				&& strcmp(root->data->file_type, "DIR") == 0) {
			int res = 1;
			res++;
		} else {
			root = root->siblings;
			while (root != NULL) {
				if (strcmp(root->data->file_name, path[index]) == 0) {
					if (strcmp(root->data->file_type, "DIR") == 0)
						break;
					else
						return LISTDIR_CANNOT_FIND_SPECIFIED_PATH_OR_DIR;
				}
				root = root->siblings;
			}
			if (root == NULL) {

				return LISTDIR_CANNOT_FIND_SPECIFIED_PATH_OR_DIR;
			}
		}
		index++;
		result = nary_list_dir(root->child, flag, filepath, filenname);
	}
	return result;
}

int nary_delete_node_final(t_node** root, file_d* data, int index) {
	static char path[PATHSIZE][CHARSIZE];
	static int tokens = 0;
	static int result_final;
	if (index == -1) {
		char* token;
		tokens = 0;
		char temp[PATHSIZE];
		strcpy(temp, data->file_path);
		token = strtok(temp, "/");
		while (token != NULL) {
			strcpy(path[tokens], token);
			token = strtok(NULL, "/");
			tokens++;
		}
		result_final = nary_delete_node_final(&(*root)->child, data, ++index);
		if (result_final == SUCCESSFUL)
			return SUCCESSFUL;
		else
			return result_final;
	}
	if (index == tokens) {
		if (*root == NULL) {
			return UNSUCCESSFUL;
		} else {
			if (strcmp((*root)->data->file_name, data->file_name) == 0) {
				if ((*root)->child != NULL)
					return NOT_EMPTY;
				*root = (*root)->siblings;
				return SUCCESSFUL;
			} else {
				t_node* resultnode;
				resultnode = (t_node*) malloc(sizeof(t_node));

				result_final = node_searchInSibling(*root, data->file_name,
						&resultnode, 0);
				if (result_final == FOUND) {
					if (resultnode->siblings->child != NULL)
						return NOT_EMPTY;
					resultnode->siblings = resultnode->siblings->siblings;
					return SUCCESSFUL;
				}
			}
		}
	}

	if (strcmp((*root)->data->file_name, path[index]) == 0) {
		result_final = nary_delete_node_final(&(*root)->child, data, ++index);

	} else {
		t_node* resultnode;
		resultnode = (t_node*) malloc(sizeof(t_node));
		result_final = node_searchInSibling(*root, path[index], &resultnode, 1);
		if (result_final == FOUND) {
			result_final = nary_delete_node_final(&(resultnode->child), data,
					++index);
		} else
			return INVALIDPATH;
	}
	return result_final;
}

int nary_delete_node(t_node** root, file_d* data) {
	int result = nary_delete_node_final(root, data, -1);
	return result;
}

int move_from_subtree(t_node* root, file_d* src_path, file_d* dest_path) {
	static char path[PATHSIZE][CHARSIZE];
	static int tokens = 0;
	static int index = -1;
	static int ret;
	static t_node *resultnode;
	int i, j;
	if (index == -1) {

		tokens = getToken(src_path->file_path, path);
		index++;
		ret = move_from_subtree(root->child, src_path, dest_path);
		if (resultnode != NULL)
			if (ret != SUCCESSFUL) {
				t_node *src_dir = resultnode;
				if (src_dir != NULL)
					index = -1;
				for (i = 0; i < tokens; i++)
					for (j = 0; j < CHARSIZE; j++)
						path[i][j] = '\0';
				tokens = getToken(dest_path->file_path, path);
				index++;
				ret = move_from_subtree(root->child, src_path, dest_path);
				if (ret == SUCCESSFUL) {
					t_node* temp;
					t_node* parent = resultnode;
					// printf("parent directory %s\n",parent->data->file_name);
					if (parent->child != NULL) {
						temp = parent->child;
						while (temp->siblings != NULL)
							temp = temp->siblings;
						temp->siblings = src_dir;
					} else
						parent->child = src_dir;
				}
			}
		return ret;
	}

	if (root != NULL) {
		if (strcmp(root->data->file_name, path[index]) == 0) {
			if (strcmp(root->data->file_type, "DIR") != 0)
				return UNSUCCESSFUL;
			if (index == tokens - 1) {
				//resultnode=(t_node*)malloc(sizeof(t_node));
				resultnode = root;
				//printf("hhvk %s",root->data->file_name);
				//printf("resultnode %s",resultnode->data->file_name);
				return SUCCESSFUL;
			} else {
				index++;
				ret = move_from_subtree(root->child, src_path, dest_path);
				return ret;
			}
		} else {
			if (root->siblings == NULL)
				return UNSUCCESSFUL;

			while (root->siblings != NULL) {
				if (strcmp(root->siblings->data->file_name, path[index]) == 0) {
					if (strcmp(root->siblings->data->file_type, "DIR") != 0)
						return UNSUCCESSFUL;
					if (index == tokens - 1) {
						resultnode = root->siblings;
						return SUCCESSFUL;
					} else {
						index++;
						root = root->siblings;
						ret = move_from_subtree(root->child, src_path,
								dest_path);
						return ret;
					}
				}
				root = root->siblings;
			}
			return UNSUCCESSFUL;

		}
	}
	return UNSUCCESSFUL;
}

t_node* tree_traversal(t_node *root, char path[][CHARSIZE], char* temp_type,
		int flag) {
	int index = 0, m = -1;
	char *f_type = NULL;
	int res = -1;
	t_node *n_node, *temp;
	f_type = (char*) malloc(sizeof(char) * 1024);
	strcpy(f_type, "DIR");
	n_node = root;
	while (strlen(path[index]) > 0) {
		if (strlen(path[index + 1]) <= 0)
			strcpy(f_type, temp_type);
		if (root != NULL) {
			if (root->child != NULL
					&& strcmp(root->child->data->file_name, path[index]) == 0
					&& strcmp(root->child->data->file_type, f_type) == 0) {
				if (flag == 0)
					n_node = root->child;
				if (flag == 1)
					n_node = root;
				m = 0;
				root = root->child;
				index++;

			} else {
				root = root->child;
				while (root != NULL) {

					if (root->siblings != NULL
							&& strcmp(root->siblings->data->file_name,
									path[index]) == 0
							&& strcmp(root->siblings->data->file_type, f_type)
									== 0) {
						if (flag == 0)
							n_node = root->siblings;
						if (flag == 1)
							n_node = root;

						index++;
						m = 1;
						root = root->siblings;
						break;
					}
					root = root->siblings;
				}
			}
		} else {
			return NULL;
		}
	}
	if (flag == 1) {
		if (m == 0) {
			temp = (n_node->child);
			//temp->siblings=NULL;
			n_node->child = n_node->child->siblings;
			temp->siblings = NULL;
			//printf("src %p %p",temp,n_node->child);
		} else if (m == 1) {
			temp = n_node->siblings;
			n_node->siblings = n_node->siblings->siblings;
			temp->siblings = NULL;
			//printf("src %p %p",temp,n_node->child);
		}
	}
	if (flag == 0) {
		temp = n_node;
		// printf("dest %p",temp);
	}
	return temp;
}

int update_path(t_node *node, int flag) {
	char temp[CHARSIZE];
	static int res = -1;

	if (node != NULL) {
		if (flag == 0) {
			if (node->child != NULL) {
				strcpy(temp, node->data->file_path);
				strcat(temp, "/");
				strcpy(node->child->data->file_path,
						strcat(temp, node->data->file_name));
			}
			node = node->child;
		}
		else if (flag == 1) {
			if (node->siblings != NULL) {
				strcpy(node->siblings->data->file_path, node->data->file_path);
			}
			node = node->siblings;
		}
		update_path(node, 0);
		update_path(node, 1);

	}
	return SUCCESSFUL;
}
void nary_move_node(t_node* root, char *srcpath, char *destpath) {
	int chk, tokens,i;
	char tem_path[CHARSIZE];
	t_node *srcnode = NULL;
	t_node *destnode = NULL;
	char srcPath[PATHSIZE][CHARSIZE];
	char destPath[PATHSIZE][CHARSIZE];
	for(i=0;i<PATHSIZE;i++){
		srcPath[i][0]='\0';
		destPath[i][0]='\0';
	}
	tokens = getToken(destpath, destPath);
	tokens = getToken(srcpath, srcPath);
	srcnode = tree_traversal(root, srcPath, "DIR", 1);
	destnode = tree_traversal(root, destPath, "DIR", 0);
	if (destnode != NULL && destnode->child != NULL) {
		destnode = destnode->child;
		while (destnode->siblings != NULL) {
			destnode = destnode->siblings;
		}
		destnode->siblings = srcnode;
		strcpy(destnode->siblings->data->file_path, destnode->data->file_path);
		chk = update_path(destnode->siblings, 0);
	/*	if (chk == 1)
			return SUCCESSFUL;*/
		//return chk;

	} else {
		if (destnode != NULL && destnode->child == NULL) {
			destnode->child = srcnode;
			strcpy(tem_path, destnode->data->file_path);
			if (strcmp(tem_path, "/") != 0)
				strcat(tem_path, "/");
			strcat(tem_path, destnode->data->file_name);
			strcpy(destnode->child->data->file_path, tem_path);
			chk = update_path(destnode->child, 0);
		/*	if (chk == 1)
				return SUCCESSFUL;*/
			//return chk;
		}
	}
}

